classdef mimic < eda
    properties
       popsize;
       varsize;
       margprob;
       fitfunc; 
       iters;
       chainindex;
       condprob;
    end
    methods
        function obj = mimic(popsize, varsize, fitfunc)
            obj.popsize = popsize;
            obj.varsize = varsize;
            obj.fitfunc = fitfunc;
            obj.iters = 0;
            obj.chainindex = 1:obj.varsize;
            obj.condprob = repmat(0.5,2, obj.varsize);
            obj.margprob = repmat(0.5,1, obj.varsize);
        end
        function res = find(obj)
            while((obj.varsize - sum(obj.margprob)) > 0.01)
                pop = mimic.sample(obj.condprob, obj.margprob, obj.chainindex, obj.popsize, obj.varsize);
                fit = obj.fitfunc.getfit(pop);
                meanfit = round(mean(fit));
                topidx = find(fit >= meanfit);
                bestpop = pop(topidx, :);
                obj.margprob = mimic.getmargprob(bestpop);    
                obj.iters = obj.iters + 1;
                ent = mimic.getentropy(obj.margprob);
                [minval, minentpidx] = min(ent);
                obj.chainindex(1) = minentpidx;
                indexes = 1 : obj.varsize;                   
                indexes(minentpidx) = [];
                for i = 2 : obj.varsize
                    [condprob, condentropy] = mimic.getcondentropX(bestpop, obj.margprob(minentpidx), minentpidx, indexes);
                    [minval minentpidx] = min(condentropy);
                    obj.chainindex(i) = indexes(minentpidx);
                    obj.condprob(:, indexes(minentpidx)) = condprob(: , minentpidx);
                    indexes(minentpidx) = [];
                end
            end            
            res = obj.margprob;
        end
    end
    methods (Static)
       function pop = sample(condprob, margprob, chainindex, popsize, varsize)
            pop = zeros(popsize, varsize);
            prevpop = ones(popsize, 1)*margprob(chainindex(1)) >= rand(popsize, 1);
            pop(:, chainindex(1)) = prevpop;
            for i=2 : varsize
                prevpop = condprob(prevpop + 1, chainindex(i)) >= rand(popsize, 1);
                pop(:, chainindex(i)) = prevpop;
            end
        end 
        
        function marprobs = getmargprob(pop)
            marprobs = (sum(pop))./(size(pop,1));
        end

        function entrp = getentropy (probs)
            inverse = 1-probs;
            entrp = -probs.*flog2(probs) - inverse.*flog2(inverse);
        end       
        
        function [condprob, condentrp] = getcondentropX(pop, probx, indx, indexes)
            condprob = mimic.getcondprobX(pop, indx, indexes);
            logy1 = flog2(condprob);
            logy0 = flog2(1 - condprob);
            condentrp = -probx.*logy1(2,:) - probx.*logy0(2,:) - (1-probx).*logy1(1,:) - (1-probx).*logy0(1,:);
        end

        function condprob = getcondprobX(pop, indx, indexes)
            xpop = pop(:, indx); 
            pop = pop(:, indexes);
            [sizex, sizey] = size(pop);
            condprob = ones(2, sizey);
            oneindx = find(xpop);
            zeroindx = find(xpop == 0);
            condprob(1, :) = (sum(pop(oneindx,   :),1))./ (size(oneindx ,1));
            condprob(2, :) = (sum(pop(zeroindx , :),1))./ (size(zeroindx,1));
        end
        
    end
end

